package com.gmrz.uaf.server;

import com.gmrz.service.challengestore.ChallengeStoreException;
import com.gmrz.service.challengestore.ChallengeStoreService;
import com.gmrz.service.challengestore.ChallengeStoreServiceDefaultImpl;
import com.gmrz.uaf.common.Constants;
import com.gmrz.uaf.common.GuiceUtil;
import com.gmrz.uaf.common.service.challengestore.UAFChallengeStoreServiceProvider;
import com.gmrz.uaf.db.DAOException;
import com.gmrz.uaf.db.UAFDAOFactory;
import com.gmrz.uaf.db.UAFDBConnectionMgr;
import com.gmrz.uaf.db.dao.UasConfigDAO;
import com.gmrz.uaf.protocol.v1.processor.exception.UAFErrorCode;
import com.gmrz.util.Strings;
import com.gmrz.util.db.DBUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;

@Singleton
public class ServerConfig {

    private static final Logger LOG = LogManager.getLogger(ServerConfig.class);

//	private String applicationID = null;

    private int challengeValiditySeconds = 300;

    private boolean licenseAccept = false;

    private String challengeStoreServiceClassName = null;

    //	private boolean isExactMatchRequired = false;
    //private boolean isUvsMatchRequired = false;
    private boolean isPluginPersistent = false;
    private int maxRegAuthenticatorsCount;
    private int logQueueMaxsize = 1000;
    private int logBatchMaxsize = 10;
    private int logWatchMillionSeconds = 500;


    private boolean whiteListSwitch = false;

//	private boolean appIdUrlBoolean = false;

    private String encryptionKey;

    private long addUviTimeOut;

    private boolean isSecurity = false;
    private int performModel = 0;
    private int scanCodeAuthCacheExpire = 300;
    private boolean tenantShare = false;
    private String uafDeviceSupportVersion = "1.0";

    public static Collection<? extends String> getAllConfigurations() {
        return Arrays.asList("uaf.application.id",
                "uaf.facet.id",
                "uaf.challenge.validity.seconds", "uaf.license.accept",
                "uaf.exact.match", "uaf.challenge.store.class.name",
                "uaf.challenge.store.redis.server.should.encrypt.data",
                "uaf.challenge.store.redis.sentinel.servers",
                "uaf.challenge.store.redis.server.master.name",
                "uaf.challenge.store.redis.server.encrypted.password",
                "uaf.challenge.store.redis.server.database.number",
                "uaf.challenge.store.redis.client.request.timeout",
                "uaf.policy.max.match.criterias.allowed",
                "uaf.reg.user.max.authenticators",
                "uaf.log.queue.maxsize",
                "uaf.log.batch.maxsize",
                "uaf.log.watch.ms",
                "uaf.whitelist.switch",
                "uaf.plugin.persistent",
                "uaf.add.uvi.time.out",
                "uaf.username.issecurity",
                "uaf.perform.model",
                "scancode.auth.cache.expire",
                "uaf.tenant.share",
                "uaf.device.support.version");
    }

    public HashSet<String> getFacetIDs(String appID)  {
        ChallengeStoreService challengeStoreService = null;
        HashMap<String, String> facetMap = new HashMap<String, String>();
        Connection conn = null;
        String facetsIdStr = null;
        HashSet<String> ids = new HashSet<String>();
        try {
            //从缓存读取facet.id
            LOG.debug("Load facet ID from cache with tenantID:{}", appID);
            UAFChallengeStoreServiceProvider challengeProvider =
                    GuiceUtil.getProcessorInjector().getInstance(UAFChallengeStoreServiceProvider.class);
            try {
                challengeStoreService = challengeProvider.get();
                if (challengeStoreService instanceof ChallengeStoreServiceDefaultImpl) {
                    LOG.debug("challengeStoreService instanceof ChallengeStoreServiceDefaultImpl");
                }
                facetsIdStr = challengeStoreService.getvalue("uaf.facet.id");

                LOG.debug("Facet ID value from cache [" + facetsIdStr + "]");
                if (!com.google.common.base.Strings.isNullOrEmpty(facetsIdStr)) {
                    LOG.debug("facetsIdStr=" + facetsIdStr);
                    facetMap = strtoHashMap(facetsIdStr);
                }
            } catch (ChallengeStoreException e) {
                LOG.error("Failed to load facet ID from cache,key=uaf.facet.id", e);
                throw new DAOException(UAFErrorCode.SYS_CACHE_FACET_FETCH_FAILED, e);
            }

            if (facetMap == null || facetMap.size() == 0) {
                try {
                    LOG.debug("Load facet ID from DB");
                    conn = UAFDBConnectionMgr.getConnection(true);
//                UasConfigDAO facetsDAO = GuiceUtil.getProcessorInjector().getInstance(DAOFactory.class)
//                        .createUasConfigDAO(conn);
                    UasConfigDAO facetsDAO = UAFDAOFactory.createUasConfigDAO(conn);
                    facetMap = facetsDAO.loadFacetsTenantID();
                    LOG.info("Facet ID value from DB=[{}] value", facetMap);
                    //challengeStoreService.putString("uaf.facet.id", facetMap.toString());
                    challengeStoreService.setRedisValue("uaf.facet.id", 86400, facetMap.toString());
                } catch (SQLException e) {
                    LOG.error("facet.id the database data read failure. ", e);
                    throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR, e);
                } catch (ChallengeStoreException e) {
                    LOG.error("facet.id the cache data write failure. ", e);
                    throw new DAOException(UAFErrorCode.SYS_CACHE_FACET_FETCH_FAILED, e);
                } finally {
                    DBUtil.close(conn);
                }
            }
            if (facetMap == null || facetMap.size() == 0) {
                throw new DAOException(UAFErrorCode.PROTOCOL_FACETID_VALIDATION_FAILED);
            }
            facetsIdStr = facetMap.get(appID);
            if (facetsIdStr == null || "".equals(facetsIdStr.trim())) {
                throw new DAOException(UAFErrorCode.PROTOCOL_FACETID_VALIDATION_FAILED);
            }
            facetsIdStr = facetsIdStr.replace(";", ",");
            LOG.debug("Successfully load facet Id = [{}] value", facetsIdStr);

        }catch (Exception e){
            LOG.error("getFacetIDs error", e);
        }
        if (facetsIdStr != null) {
            String[] tokens = facetsIdStr.split(",");
            if (tokens != null) {
                for (String t : tokens) {
                    ids.add(t);
                }
            } else {
                ids.add(facetsIdStr);
            }
        }
        return ids;
    }
    private HashMap<String, String> strtoHashMap(String strvalue) {
        strvalue = strvalue.substring(1, strvalue.length() - 1);
        String[] strs = strvalue.split("\\,");
        HashMap<String, String> m = new HashMap<String, String>();
        for (String s : strs) {
            String[] ms = s.split("\\=");
            if (ms.length >= 2) {
                m.put(ms[0].trim(), ms[1].replace(";", ",").trim());
            }
        }
        return m;
    }
    public int getChallengeValiditySeconds() {
        return this.challengeValiditySeconds;
    }

    public boolean getLicenseAccept() {
        return this.licenseAccept;
    }

    public String getChallengeStoreServiceClassName() {
        return this.challengeStoreServiceClassName;
    }

    public boolean isExactMatchRequired(String tenantId) {
        String isExactMatchRequired = getConfigByTenantId(Constants.UAP_EXACT_MATCH_CACHE_KEY, tenantId);
        return Boolean.parseBoolean(isExactMatchRequired);
    }

    public int getMaxRegAuthenticatorsCount() {

        return this.maxRegAuthenticatorsCount;
    }

    public boolean isUvsMatchRequired(String tenantId) {
        String isUvsMatchRequired = getConfigByTenantId(Constants.UAP_UVS_MATCH_CACHE_KEY, tenantId);
        return Boolean.parseBoolean(isUvsMatchRequired);
    }

    public boolean isPluginPersistent() {
        return isPluginPersistent;
    }

    public boolean getAppIdUrlBoolean(String tenantId,String transType) {
        if (getApplicationID(tenantId,transType).contains(Constants.APP_ID)) {
            return true;
        } else {
            return false;
        }
    }

    @Inject
    public void setPluginPersistent(@Named("uaf.plugin.persistent") boolean pluginPersistent) {
        isPluginPersistent = pluginPersistent;
    }

    public String getApplicationID(String tenantId,String transType) {
        return this.getConfigByTenantIdAndTransType(Constants.UAP_APP_ID_CACHE_KEY, tenantId,transType);
    }

    public String getConfigByTenantIdAndTransType(String key, String tenantId,String transType){
        String config = null;
        UAFChallengeStoreServiceProvider uafChallengeStoreServiceProvider = GuiceUtil.getProcessorInjector()
                .getInstance(UAFChallengeStoreServiceProvider.class);
        ChallengeStoreService challengeStoreService = uafChallengeStoreServiceProvider.get();
        Connection conn = null;
        try {
            String cacheKey = key + tenantId + "."+transType;
            config = challengeStoreService.getRedisValue(cacheKey);
            if (null == config) {
                conn = UAFDBConnectionMgr.getConnection();
                UasConfigDAO facetsDAO = UAFDAOFactory.createUasConfigDAO(conn);
                String vlaue = facetsDAO.getConfigByTenantIdAndTransType(key, tenantId,transType);
                LOG.warn("getConfigByTenantId from db key:" + cacheKey + ",value:" + vlaue);
                if (null == vlaue) {
                    vlaue = "";
                }
                challengeStoreService.putString(cacheKey, vlaue);
                config = vlaue;
            }

        } catch (Exception e) {
            LOG.error("getConfigByTenantId error", e);
        } finally {
            DBUtil.close(conn);
        }
        return config;
    }

    @Inject
    private void setChallengeValiditySeconds(
            @Named("uaf.challenge.validity.seconds") String seconds) {
        this.challengeValiditySeconds = Integer.parseInt(seconds);
    }

    @Inject
    private void setLicenseAccept(
            @Named("uaf.license.accept") boolean licenseAccept) {
        this.licenseAccept = licenseAccept;
    }

    @Inject
    private void setchallengeStoreServiceClassName(
            @Named("uaf.challenge.store.class.name") String challengeStoreclassName) {
        this.challengeStoreServiceClassName = challengeStoreclassName;
    }

    @Inject
    private void setMaxRegAuthenticatorsCount(
            @Named("uaf.reg.user.max.authenticators") String maxRegAuthenticatorsCount) {
        this.maxRegAuthenticatorsCount = Integer
                .parseInt(maxRegAuthenticatorsCount);
    }

    public int getLogQueueMaxsize() {
        return logQueueMaxsize;
    }

    @Inject
    public void setLogQueueMaxsize(@Named("uaf.log.queue.maxsize") String logQueueMaxsize) {
        this.logQueueMaxsize = Integer.parseInt(logQueueMaxsize);
    }

    public int getLogBatchMaxsize() {
        return logBatchMaxsize;
    }

    @Inject
    public void setLogBatchMaxsize(@Named("uaf.log.batch.maxsize") String logBatchMaxsize) {
        this.logBatchMaxsize = Integer.parseInt(logBatchMaxsize);
    }

    public int getLogWatchMillionSeconds() {
        return logWatchMillionSeconds;
    }

    @Inject
    public void setLogWatchMillionSeconds(@Named("uaf.log.watch.ms") String logWatchMillionSeconds) {
        this.logWatchMillionSeconds = Integer.parseInt(logWatchMillionSeconds);
    }

    public boolean isWhiteListSwitch() {
        return whiteListSwitch;
    }

    @Inject
    public void setWhiteListSwitch(@Named("uaf.whitelist.switch") boolean whiteListSwitch) {
        this.whiteListSwitch = whiteListSwitch;
    }

    @Inject
    public void setEncryptionKey(@Named("uaf.default.external.encryption.key") String encryptionKey) {
        this.encryptionKey = encryptionKey;
    }

    public String getEncryptionKey() {
        return this.encryptionKey;
    }

    @Inject
    public void setAddUviTimeOut(@Named("uaf.add.uvi.time.out") long addUviTimeOut) {
        this.addUviTimeOut = addUviTimeOut;
    }

    public long getAddUviTimeOut() {
        return this.addUviTimeOut;
    }

    public boolean isSecurity() {
        return isSecurity;
    }

    @Inject
    public void setSecurity(@Named("uaf.username.issecurity") boolean security) {
        isSecurity = security;
    }

    /**
     *  查询 t_tenant_config 配置数据
     * @param key
     * @param tenantId
     */
    public String getAppSecretByTenantId(String key, String tenantId){
        return this.getConfigByTenantId(key,tenantId);
    }


    public String getConfigByTenantId(String key, String tenantId) {
        String config = null;
        UAFChallengeStoreServiceProvider uafChallengeStoreServiceProvider = GuiceUtil.getProcessorInjector()
                .getInstance(UAFChallengeStoreServiceProvider.class);
        ChallengeStoreService challengeStoreService = uafChallengeStoreServiceProvider.get();
        Connection conn = null;
        try {
            String cacheKey = key + tenantId;
            config = challengeStoreService.getRedisValue(cacheKey);
            if (null == config) {
                conn = UAFDBConnectionMgr.getConnection();
                UasConfigDAO facetsDAO = UAFDAOFactory.createUasConfigDAO(conn);
                String vlaue = facetsDAO.getConfigByTenantId(key, tenantId);
                LOG.warn("getConfigByTenantId from db key:" + cacheKey + ",value:" + vlaue);
                if (null == vlaue) {
                    vlaue = "";
                }
                challengeStoreService.putString(cacheKey, vlaue);
                config = vlaue;
            }

        } catch (Exception e) {
            LOG.error("getConfigByTenantId error", e);
        } finally {
            DBUtil.close(conn);
        }
        return config;
    }

    public int getPerformModel() {
        return performModel;
    }

    @Inject
    public void setPerformModel(@Named("uaf.perform.model") int performModel) {
        this.performModel = performModel;
    }

    public int getScanCodeAuthCacheExpire() {
        return scanCodeAuthCacheExpire;
    }

    @Inject
    public void setScanCodeAuthCacheExpire(@Named("scancode.auth.cache.expire") int scanCodeAuthCacheExpire) {
        this.scanCodeAuthCacheExpire = scanCodeAuthCacheExpire;
    }

    public boolean getTenantShare() {
        try {
            return Boolean.parseBoolean(getUasConfig("uaf.tenant.share"));
        } catch (Exception e) {
            LOG.error("getTenantShare error",e);
        }
        return false;
    }

    @Inject
    public void setTenantShare(@Named("uaf.tenant.share") boolean tenantShare) {
        this.tenantShare = tenantShare;
    }

    public String getUasConfig(String key) throws ChallengeStoreException, DAOException {
        UAFChallengeStoreServiceProvider uafChallengeStoreServiceProvider = GuiceUtil.getProcessorInjector()
                .getInstance(UAFChallengeStoreServiceProvider.class);
        ChallengeStoreService challengeStoreService = uafChallengeStoreServiceProvider.get();
        String value = challengeStoreService.getRedisValue(key);
        if (null == value) {
            Connection conn = null;
            try {
                conn = UAFDBConnectionMgr.getConnection();
                UasConfigDAO uasConfigDAO = UAFDAOFactory.createUasConfigDAO(conn);
                String daoValue = uasConfigDAO.getConfigByKey(key);
                value = daoValue;
                if (Strings.isNullOrEmpty(daoValue)) {
                    daoValue = " ";
                }
                challengeStoreService.putString(key, daoValue);

            } catch (SQLException sqle) {
                throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR, sqle);
            } finally {
                DBUtil.close(conn);
            }
        }
        return value;
    }

    public String getUafDeviceSupportVersion() {
        return uafDeviceSupportVersion;
    }
    @Inject
    public void setUafDeviceSupportVersion(@Named("uaf.device.support.version") String uafDeviceSupportVersion) {
        this.uafDeviceSupportVersion = uafDeviceSupportVersion;
    }

    public String getDictConfig(String key) {
        String config = null;
        UAFChallengeStoreServiceProvider uafChallengeStoreServiceProvider = GuiceUtil.getProcessorInjector()
                .getInstance(UAFChallengeStoreServiceProvider.class);
        ChallengeStoreService challengeStoreService = uafChallengeStoreServiceProvider.get();
        Connection conn = null;
        try {
            String cacheKey = Constants.UAF_SECURITY_LEVEL_KEY+key;
            config = challengeStoreService.getRedisValue(cacheKey);
            if (null == config) {
                conn = UAFDBConnectionMgr.getConnection();
                UasConfigDAO facetsDAO = UAFDAOFactory.createUasConfigDAO(conn);
                String vlaue = facetsDAO.getDictConfig(key);
                LOG.warn("getDictConfig from db key:" + key + ",value:" + vlaue);
                if (null == vlaue) {
                    vlaue = "";
                }
                challengeStoreService.putString(cacheKey, vlaue);
                config = vlaue;
            }

        } catch (Exception e) {
            LOG.error("getConfigByTenantId error", e);
        } finally {
            DBUtil.close(conn);
        }
        return config;
    }

    public String getRpID(String appID){
        return getConfigByTenantId(Constants.UAF_RPID,appID);
    }

    public String getPolicyConfig(String appID,String authType,String transType,String key){
        String config = null;
        UAFChallengeStoreServiceProvider uafChallengeStoreServiceProvider = GuiceUtil.getProcessorInjector()
                .getInstance(UAFChallengeStoreServiceProvider.class);
        ChallengeStoreService challengeStoreService = uafChallengeStoreServiceProvider.get();
        Connection conn = null;
        try {
            String cacheKey = Constants.UAF_POLICY_CONFIG_KEY+appID+"."+authType+"."+transType+"."+key;
            config = challengeStoreService.getRedisValue(cacheKey);
            if (null == config) {
                conn = UAFDBConnectionMgr.getConnection();
                UasConfigDAO facetsDAO = UAFDAOFactory.createUasConfigDAO(conn);
                String vlaue = facetsDAO.getPolicyConfig(appID,authType,transType,key);
                LOG.warn("getPolicyConfig from db key:" + cacheKey + ",value:" + vlaue);
                if (null == vlaue) {
                    vlaue = "";
                }
                challengeStoreService.putString(cacheKey, vlaue);
                config = vlaue;
            }

        } catch (Exception e) {
            LOG.error("getConfigByTenantId error", e);
        } finally {
            DBUtil.close(conn);
        }
        return config;
    }
}
